#include "Subsystems/SubsystemPickables.h"
#include "Subsystems/SubsystemGameInfo.h"
#include "Subsystems/SubsystemTerrain.h"
#include "Subsystems/SubsystemTimeOfDay.h"
#include "Subsystems/BlockBehaviors/SubsystemBlockBehaviors.h"
#include "Subsystems/BlockBehaviors/SubsystemWaterBlockBehavior.h"

#include "Network/Packages/PickablePackage.h"
#include "Network/PackageManager.h"

#include "Managers/GameManager.h"
#include "Managers/BlocksManager.h"
#include "Managers/ChangeManager.h"

#include "Components/ComponentPlayer.h"
#include "Components/ComponentHealth.h"
#include "Components/ComponentBody.h"
#include "Components/ComponentMiner.h"
#include "Components/ComponentInventoryBase.h"
#include "Components/ComponentInventory.h"
#include "Components/ComponentLevel.h"
#include "Components/ComponentCreativeInventory.h"

#include "Player/PlayerData.h"

#include "Blocks/WaterBlock.h"
#include "Blocks/ExperienceBlock.h"

#include "Log.h"
#include "GameSingleton.h"
#include "GameRandom.hpp"
#include "GameRegistry.hpp"
#include "GameThreadPool.hpp"

namespace PocketSurvival
{
    static Pickable basePickablesArray[UINT16_MAX];

    SubsystemPickables::SubsystemPickables()
    {
        m_pickableIdStack.setSize(UINT16_MAX);
        for (uint16_t index = 0; index < UINT16_MAX; index++)
        {
            basePickablesArray[index].id = index;
            m_pickableIdStack.push(UINT16_MAX - 1 - index);
            m_pickablesTab[index] = &(basePickablesArray[index]);
        }
    }

    SubsystemPickables::~SubsystemPickables()
    {
        m_pickableIdsSet.clear();
    }

    // void SubsystemPickables::recyclePickable(Pickable *pickable)
    // {
    //     if (pickable != nullptr)
    //     {
    //         m_pickableIdStack.push(pickable->id);
    //         delete pickable;
    //     }
    // }

    // 玩家站在物品旁边处理
    void SubsystemPickables::onPlayerGetPickable(Pickable *pickable, ComponentPlayer *tmpPlayer, const Vector3 &positionFix, float distance)
    {
        // 是否是经验球
        bool flag = (Terrain::ExtractContents(pickable->value) == ExperienceBlock::Index());

        int32_t valueSlot = -1;
        if (tmpPlayer->playerData->gameMode == GameMode::Creative)
        {
            valueSlot = ComponentInventoryBase::FindAcquireSlotForItem(tmpPlayer->componentCreativeInventory, pickable->value);
        }
        else
        {
            valueSlot = ComponentInventoryBase::FindAcquireSlotForItem(tmpPlayer->componentInventory, pickable->value);
        }

        // IInventory *inventory = tmpPlayer->componentMiner->inventory;
        // int32_t valueSlot = ComponentInventoryBase::FindAcquireSlotForItem(inventory, pickable->value);

        if (flag || valueSlot >= 0)
        {
            if (distance < 1.0f)
            {
                if (flag)
                {
                    tmpPlayer->componentLevel->addExperience(pickable->count, true);
                    pickable->toRemove = true;
                }
                else
                {
                    if (tmpPlayer->playerData->gameMode == GameMode::Creative)
                    {
                        pickable->count = ComponentInventoryBase::AcquireItems(tmpPlayer->componentCreativeInventory, pickable->value, pickable->count);
                    }
                    else
                    {
                        pickable->count = tmpPlayer->componentInventory->addItemFast(pickable->value, pickable->count);
                    }

                    // pickable->count = ComponentInventoryBase::AcquireItems(inventory, pickable->value, pickable->count);
                    
                    if (pickable->count == 0)
                    {
                        pickable->toRemove = true;
                    }
                }
            }
            else if (pickable->stuckMatrix == nullptr)
            {
                Vector3 velocity;
                tmpPlayer->componentBody->getVelocity(velocity);

                pickable->flyToPosition = std::make_shared<Vector3>(positionFix + 0.1f * MathUtils::Sqrt<float>(distance) * velocity);
                GameSingleton::packageManager->addSendPackageToQueue(std::make_shared<PickablePackage>(pickable, PickablePackageType::SetFlyToPosition));
            }
        }
    }

    // 添加新的掉落物品
    Pickable *SubsystemPickables::addPickable(int32_t cellValue, uint32_t count, const Vector3 &position, std::shared_ptr<Vector3> velocity, std::shared_ptr<Matrix> stuckMatrix)
    {
        Pickable *pickable = createPickable(cellValue, count, position, velocity, stuckMatrix);
        GameSingleton::changeManager->pickableCreative(pickable);
        return pickable;
    }

    // 创建掉落物
    Pickable *SubsystemPickables::createPickable(int32_t cellValue, uint32_t count, const Vector3 &position, std::shared_ptr<Vector3> velocity, std::shared_ptr<Matrix> stuckMatrix)
    {
        std::lock_guard<SpinLock> lk(m_pickableMutex);

        if (m_pickableIdStack.getStackSize() <= 0)
        {
            throw std::runtime_error("无法增加新的掉落物品ID");
        }
        uint16_t id = m_pickableIdStack.top();
        m_pickableIdStack.pop();
        Pickable *pickable = m_pickablesTab[id];

        // Log::Info(fmt::format("create pickable id : {}", pickable->id));

        pickable->value = cellValue;
        pickable->count = count;
        pickable->position = position;
        pickable->stuckMatrix = stuckMatrix;
        pickable->creationTime = m_info->totalElapsedGameTime;
        pickable->toRemove = false;

        if (velocity != nullptr)
        {
            pickable->velocity = *(velocity);
        }
        else if (Terrain::ExtractContents(cellValue) == 248)
        {
            Vector2 vector2 = GameSingleton::gameRandom->getVector2(1.5f, 2.0f);
            pickable->velocity = Vector3(vector2.posX, 3.0f, vector2.posY);
        }
        else
        {
            pickable->velocity = Vector3(GameSingleton::gameRandom->getFloat(-0.5f, 0.5f), GameSingleton::gameRandom->getFloat(1.0f, 1.2f), GameSingleton::gameRandom->getFloat(-0.5f, 0.5f));
        }

        m_pickableIdsSet.insert(id);
        return pickable;
    }

    Pickable *SubsystemPickables::getPickableById(uint16_t id)
    {
        // std::lock_guard<SpinLock> lk(m_pickableMutex);
        if (id < UINT16_MAX)
        {
            Pickable *pickable = m_pickablesTab[id];
            if (pickable->toRemove == false)
            {
                return pickable;
            }
        }
        return nullptr;
    }

    uint32_t SubsystemPickables::clearAllPickable()
    {
        std::lock_guard<SpinLock> lk(m_pickableMutex);
        uint32_t num = 0;
        for (uint16_t id : m_pickableIdsSet)
        {
            if (m_pickablesTab[id]->toRemove == false)
            {
                m_pickablesTab[id]->toRemove = true;
                num++;
            }
        }
        return num;
    }

    void SubsystemPickables::getNowPickables(std::vector<Pickable *> &vec)
    {
        std::lock_guard<SpinLock> lk(m_pickableMutex);
        for (uint16_t id : m_pickableIdsSet)
        {
            vec.push_back(m_pickablesTab[id]);
        }
    }

    void SubsystemPickables::getNowNotRemovePickables(std::vector<Pickable *> &vec)
    {
        std::lock_guard<SpinLock> lk(m_pickableMutex);
        for (uint16_t id : m_pickableIdsSet)
        {
            if (m_pickablesTab[id]->toRemove == false)
            {
                vec.push_back(m_pickablesTab[id]);
            }
        }
    }

    void SubsystemPickables::update(float dt)
    {
        static std::function<bool(int32_t, float)> func = [this](int32_t value, float) -> bool
        {
            return GameSingleton::blocksManager->blocks[Terrain::ExtractContents(value)]->IsCollidable;
        };

        double totalElapsedGameTime = m_info->totalElapsedGameTime;
        float num = MathUtils::Pow(0.5f, dt);
        float num2 = MathUtils::Pow(0.001f, dt);

        static std::vector<ComponentPlayer *> playerVec;
        playerVec.clear();

        // 获取所有玩家的对象，用于判断是否能拾取物品
        auto view = GameSingleton::gameRegistry->view<ComponentPlayer *>();
        for (auto entity : view)
        {
            ComponentPlayer *componentPlayer = view.get<ComponentPlayer *>(entity);
            if (componentPlayer->componentHealth->health > 0.0f)
            {
                playerVec.push_back(componentPlayer);
            }
        }

        // std::lock_guard<std::mutex> lk(m_pickableMutex);

        m_pickableMutex.lock();
        std::set<uint16_t> pickableIdsSetCopy = m_pickableIdsSet;
        m_pickableMutex.unlock();


        // 物品更新
        for (uint16_t id : pickableIdsSetCopy)
        {
            Pickable *pickable = m_pickablesTab[id];
            // 判断掉落物是否是需要移除的
            if (pickable->toRemove == true)
            {
                // Log::Info(fmt::format("remove pickable id : {}", pickable->id));

                m_pickablesToRemove.push_back(pickable->id);
                pickable->stuckMatrix = nullptr;
                pickable->flyToPosition = nullptr;
                continue;
            }
            Block *block = GameSingleton::blocksManager->blocks[Terrain::ExtractContents(pickable->value)];

            float num4 = MathUtils::Lerp(300.0f, 90.0f, MathUtils::Saturate(pickableIdsSetCopy.size() / 60.0f));
            double num5 = totalElapsedGameTime - pickable->creationTime;
            if (num5 > num4) //  || pickable->position.posY < 0.0f)
            {
                pickable->toRemove = true;
            }
            else
            {
                TerrainChunk *chunkAtCell = m_subsystemTerrain->getTerrainChunk(Terrain::ToCell(pickable->position.posX) >> 4, Terrain::ToCell(pickable->position.posZ) >> 4);
                if (chunkAtCell != nullptr && chunkAtCell->State > TerrainChunkState::InvalidContents4)
                {
                    Vector3 position = pickable->position;

                    Vector3 vector = position + pickable->velocity * dt;
                    if (pickable->flyToPosition == nullptr && num5 > 0.5)
                    {
                        for (auto playerIt = playerVec.begin(); playerIt != playerVec.end(); ++playerIt)
                        {
                            ComponentBody *componentBody = (*playerIt)->componentBody;

                            Vector3 position;
                            componentBody->getPosition(position);
                            Vector3 v = position + Vector3(0.0f, 0.75f, 0.0f);

                            float num6 = (v - pickable->position).lengthSquared();
                            if (num6 < 3.0625f)
                            {
                                onPlayerGetPickable(pickable, *playerIt, v, num6);
                            }
                        }
                    }
                    if (pickable->flyToPosition != nullptr)
                    {
                        Vector3 v2 = *(pickable->flyToPosition) - pickable->position;
                        float num7 = v2.lengthSquared();
                        if (num7 >= 0.25f)
                        {
                            pickable->velocity = 6.0f * v2 / MathUtils::Sqrt<float>(num7);
                        }
                        else
                        {
                            pickable->flyToPosition = nullptr;
                        }
                    }
                    else
                    {
                        FluidBlock *surfaceBlock = nullptr;
                        std::shared_ptr<float> surfaceHeight = nullptr;
                        std::shared_ptr<Vector2> vector2 = m_waterBlockBehavior->calculateFlowSpeed(Terrain::ToCell(pickable->position.posX), Terrain::ToCell(pickable->position.posY + 0.1f), Terrain::ToCell(pickable->position.posZ), surfaceBlock, surfaceHeight);
                        if (pickable->stuckMatrix == nullptr)
                        {
                            std::shared_ptr<TerrainRaycastResult> terrainRaycastResult = m_subsystemTerrain->raycast(position, vector, false, true, func);
                            if (terrainRaycastResult != nullptr)
                            {
                                int32_t contents = Terrain::ExtractContents(m_subsystemTerrain->getCellValue(terrainRaycastResult->cellFace.point.posX, terrainRaycastResult->cellFace.point.posY, terrainRaycastResult->cellFace.point.posZ));
                                std::vector<SubsystemBlockBehavior *> &blockBehaviors = m_blockBehaviors->getBlockBehaviors(contents);
                                for (auto behaviorIt = blockBehaviors.begin(); behaviorIt != blockBehaviors.end(); ++behaviorIt)
                                {
                                    (*behaviorIt)->onHitByProjectile(terrainRaycastResult->cellFace, pickable);
                                }
                                if (m_subsystemTerrain->raycast(position, position, false, true, func) != nullptr)
                                {
                                    int32_t num8 = Terrain::ToCell(position.posX);
                                    int32_t num9 = Terrain::ToCell(position.posY);
                                    int32_t num10 = Terrain::ToCell(position.posZ);
                                    int32_t num11 = 0;
                                    int32_t num12 = 0;
                                    int32_t num13 = 0;
                                    std::shared_ptr<int32_t> num14 = nullptr;
                                    for (int32_t j = -3; j <= 3; j++)
                                    {
                                        for (int32_t k = -3; k <= 3; k++)
                                        {
                                            for (int32_t l = -3; l <= 3; l++)
                                            {
                                                if (false == GameSingleton::blocksManager->blocks[Terrain::ExtractContents(m_subsystemTerrain->getCellValue(j + num8, k + num9, l + num10))]->IsCollidable)
                                                {
                                                    int32_t num15 = j * j + k * k + l * l;
                                                    if (num14 == nullptr || num15 < *num14)
                                                    {
                                                        num11 = j + num8;
                                                        num12 = k + num9;
                                                        num13 = l + num10;
                                                        num14 = std::make_shared<int32_t>(num15);
                                                    }
                                                }
                                            }
                                        }
                                    }
                                    if (num14 != nullptr)
                                    {
                                        pickable->flyToPosition = std::make_shared<Vector3>(Vector3(num11, num12, num13) + Vector3(0.5f));
                                    }
                                    else
                                    {
                                        pickable->toRemove = true;
                                    }
                                }
                                else
                                {
                                    Plane plane = terrainRaycastResult->cellFace.calculatePlane();
                                    bool flag2 = vector2 != nullptr && *vector2 != FastVector2::Zero;
                                    if (plane.Normal.posX != 0.0f)
                                    {
                                        float num16 = (flag2 || MathUtils::Sqrt<float>(MathUtils::Sqr<float>(pickable->velocity.posY) + MathUtils::Sqr<float>(pickable->velocity.posZ)) > 10.0f) ? 0.95f : 0.25f;
                                        pickable->velocity *= Vector3(0.0f - num16, num16, num16);
                                    }
                                    if (plane.Normal.posY != 0.0f)
                                    {
                                        float num17 = (flag2 || MathUtils::Sqrt<float>(MathUtils::Sqr<float>(pickable->velocity.posX) + MathUtils::Sqr<float>(pickable->velocity.posZ)) > 10.0f) ? 0.95f : 0.25f;
                                        pickable->velocity *= Vector3(num17, 0.0f - num17, num17);
                                        if (flag2)
                                        {
                                            pickable->velocity.posY += 0.1f * plane.Normal.posY;
                                        }
                                    }
                                    if (plane.Normal.posZ != 0.0f)
                                    {
                                        float num18 = (flag2 || MathUtils::Sqrt<float>(MathUtils::Sqr<float>(pickable->velocity.posX) + MathUtils::Sqr(pickable->velocity.posY)) > 10.0f) ? 0.95f : 0.25f;
                                        pickable->velocity *= Vector3(num18, num18, 0.0f - num18);
                                    }
                                    vector = position;
                                }
                            }
                        }
                        else
                        {
                            Vector3 vector3 = pickable->stuckMatrix->getTranslation() + pickable->stuckMatrix->getUp() * block->ProjectileTipOffset;
                            if (m_subsystemTerrain->raycast(vector3, vector3, false, true, func) == nullptr)
                            {
                                pickable->position = pickable->stuckMatrix->getTranslation();
                                pickable->velocity = FastVector3::Zero;
                                pickable->stuckMatrix = nullptr;
                            }
                        }

                        // 未完成...
                        // if (m_subsystemTime.PeriodicGameTimeEvent(1.0, (double)(pickable.GetHashCode() % 100) / 100.0) && (m_subsystemTerrain.Terrain.GetCellContents(Terrain.ToCell(pickable.Position.X), Terrain.ToCell(pickable.Position.Y + 0.1f), Terrain.ToCell(pickable.Position.Z)) == 104 || m_subsystemFireBlockBehavior.IsCellOnFire(Terrain.ToCell(pickable.Position.X), Terrain.ToCell(pickable.Position.Y + 0.1f), Terrain.ToCell(pickable.Position.Z))))
                        // {
                        // 	m_subsystemAudio.PlayRandomSound("Audio/Sizzles", 1f, GameSingleton::gameRandom->Float(-0.2f, 0.2f), pickable.Position, 3f, autoDelay: true);
                        // 	pickable.ToRemove = true;
                        // 	m_subsystemExplosions.TryExplodeBlock(Terrain.ToCell(pickable.Position.X), Terrain.ToCell(pickable.Position.Y), Terrain.ToCell(pickable.Position.Z), pickable.Value);
                        // }

                        if (pickable->stuckMatrix == nullptr)
                        {
                            if (vector2 != nullptr && surfaceHeight != nullptr)
                            {
                                float num19 = *surfaceHeight - pickable->position.posY;
                                float num20 = MathUtils::Saturate(3.0f * num19);
                                pickable->velocity.posX += 4.0f * dt * (vector2->posX - pickable->velocity.posX);
                                pickable->velocity.posY -= 10.0f * dt;
                                pickable->velocity.posY += 10.0f * (1.0f / block->Density * num20) * dt;
                                pickable->velocity.posZ += 4.0f * dt * (vector2->posY - pickable->velocity.posZ);
                                pickable->velocity.posY *= num2;
                            }
                            else
                            {
                                pickable->velocity.posY -= 10.0f * dt;
                                pickable->velocity *= num;
                            }
                        }
                    }
                    pickable->position = vector;
                }
            }
        }

        
        if (m_pickablesToRemove.size() > 0)
        {
            for (uint16_t id : m_pickablesToRemove)
            {
                m_pickableMutex.lock();

                auto it = m_pickableIdsSet.find(id);
                if (it != m_pickableIdsSet.end())
                {
                    m_pickableIdsSet.erase(it);
                    GameSingleton::packageManager->addSendPackageToQueue(std::make_shared<PickablePackage>(id));
                    m_pickableIdStack.push(id);
                }

                m_pickableMutex.unlock();
            }
            m_pickablesToRemove.clear();
        }


        if (m_timeOfDay->periodicGameTimeEvent(5.0, 0.0))
        {
            if (m_pickableIdsSet.size() > 0)
            {
                GameSingleton::packageManager->addSendPackageToQueue(std::make_shared<PickablePackage>(PickablePackageType::Update));
            }
        }
    }

    void SubsystemPickables::internalSave()
    {
        nlohmann::json pickableArrayJson = nlohmann::json::array();

        m_pickableMutex.lock();
        for (uint16_t id : m_pickableIdsSet)
        {
            Pickable *pickable = m_pickablesTab[id];
            nlohmann::json pickableJson;
            pickableJson["Value"] = pickable->value;
            pickableJson["Count"] = pickable->count;
            pickableJson["Position"] = pickable->position;
            pickableJson["Velocity"] = pickable->velocity;
            pickableJson["CreationTime"] = pickable->creationTime;
            if (pickable->stuckMatrix != nullptr)
            {
                pickableJson["StuckMatrix"] = *(pickable->stuckMatrix);
            }
            pickableArrayJson.push_back(pickableJson);
        }
        m_pickableMutex.unlock();

        std::string pickableArrayFile = fmt::format("{}/Pickables.json", GameSingleton::gameManager->getNowWorldPath());
        if(Utils::SaveTextFile(pickableArrayFile, pickableArrayJson.dump()) == false)
        {
            Log::Error(fmt::format("无法保存Pickable数据到文件: {}", pickableArrayFile));
            // throw std::runtime_error(fmt::format("无法保存Pickable数据到文件: {}", pickableArrayFile)); 
        }
    }

    void SubsystemPickables::load(const nlohmann::json &json)
    {
        m_blockBehaviors = GameSingleton::singletonPtr->getSingleton<SubsystemBlockBehaviors>();
        m_waterBlockBehavior = GameSingleton::singletonPtr->getSingleton<SubsystemWaterBlockBehavior>();
        m_subsystemTerrain = GameSingleton::singletonPtr->getSingleton<SubsystemTerrain>();
        m_info = GameSingleton::singletonPtr->getSingleton<SubsystemGameInfo>();
        m_timeOfDay = GameSingleton::singletonPtr->getSingleton<SubsystemTimeOfDay>();

        std::string jsonText;
        if(Utils::LoadTextFile(fmt::format("{}/Pickables.json", GameSingleton::gameManager->getNowWorldPath()), jsonText) == true)
        {
            nlohmann::json pickableArrayJson = nlohmann::json::parse(jsonText);
            for(const nlohmann::json &pickableJson : pickableArrayJson)
            {
                int32_t cellValue = pickableJson["Value"].get<int32_t>();
                uint32_t count = pickableJson["Count"].get<uint32_t>();
                Vector3 position = pickableJson["Position"].get<Vector3>();
                Vector3 velocity = pickableJson["Velocity"].get<Vector3>();
                double creationTime = pickableJson["CreationTime"].get<double>();
                std::shared_ptr<Matrix> stuckMatrix = nullptr;
                auto it = pickableJson.find("StuckMatrix");
                if (it != pickableJson.end())
                {
                    stuckMatrix = std::make_shared<Matrix>(it->get<Matrix>());
                }
                Pickable *pickable = createPickable(cellValue, count, position, std::make_shared<Vector3>(velocity), stuckMatrix);
                pickable->creationTime = creationTime;
            }
        }
    }

    void SubsystemPickables::save(nlohmann::json &json)
    {
        // 交由线程池进行保存处理
        GameSingleton::gameSaveThreadPool->push_task(std::bind(&SubsystemPickables::internalSave, this));
    }

    void SubsystemPickables::saveToMsgJson(nlohmann::json &jsonData) {}

    static const std::string subsystemName = "Pickables";
    const std::string &SubsystemPickables::getName() const
    {
        return subsystemName;
    }
    SubUpdateType SubsystemPickables::getUpdateType()
    {
        return SubUpdateType::MultiThreadUpdate;
    }
}